/*------------------------------------------------------------------------------
$module:      files
$package:     CseApi
$purpose:     register, binary file and filename/path process
$author:      WAYNE WORK STDIO
$CreateAt:    10/05/21
$LastModify:
$license:     Mozilla Public License Version 1.1
------------------------------------------------------------------------------*/

#include "CseApi/platform.h"

#include "../files.h"
#include "CseApi/CharArray.h"

#define CSE_HOME_DIR_NAME          _T("CSE_HOME")

#if defined(__VISUALC__) || defined(__MINGW32__)
  #include <windows.h>
  #include <winreg.h>

  extern const long HKey_ClassesRoot;    // HKEY_CLASSES_ROOT
  extern const long HKey_CurrentUser;    // HKEY_CURRENT_USER
  extern const long HKey_users;          // HKEY_USERS
  extern const long HKey_LocalMachine;   // HKEY_LOCAL_MACHINE;

  typedef struct
  {
      TXchar *path;
      long hRootKey;
      HKEY hKey;
  } WIN32_REG_STRUCT;

  #define CSE_KERNEL_REG_ID        _T("Software\\cse soft\\CseKernel21")
  #define WINCE_CSE_LIB_PATH       _T("Program Files\\cse21\\lib\\")
#else
  #error "Compiler not support yet!"
#endif

TCharArray GetCurrExeFileName()
{
    TXchar buff[260];
    GetModuleFileName(0,buff,260);
    return TCharArray(buff);
}

TCharArray ChangeFileExt(const TCharArray& sFile, const TCharArray& ext)
{
    TCharArray ret = sFile;

    for (int i = ret.len()-1; i >= 0; i--)
    {
        TXchar ch = ret[i];
        if (ch == _T('.'))
        {
            ret.truncate(i);
            break;
        }
        else if (ch == _T('\\') || ch == _T('/') || ch == _T(':'))
            break;
    }
    ret += ext;
    return ret;
}

TCharArray ExtractFileExt(const TCharArray& FileName)
{
    TCharArray ret;
    for (int i = FileName.len()-1; i >= 0; i--)
    {
        TXchar ch = FileName[i];
        if (ch == _T('.'))
            return ret;
        else if (ch == _T('\\') || ch == _T('/'))
            break;
        else ret = ch + ret;
    }

    ret.clear();
    return ret;
}

TCharArray ExtractFileName(const TCharArray& FileName)
{
    TCharArray ret;
    for (int i = FileName.len()-1; i >= 0; i--)
    {
        TXchar ch = FileName[i];
        if (ch == _T('\\') || ch == _T('/'))
            break;
        else ret = ch + ret;
    }
    return TCharArray(CUT_FROM,ret);
}

TCharArray ExtractFileDir(const TCharArray& FileName)
{
    TCharArray ret = FileName;
    for (int i = ret.len()-1; i >= 0; i--)
    {
        TXchar ch = FileName[i];
        if (ch == _T('\\') || ch == _T('/'))
        {
            ret.truncate(i);
            return TCharArray(CUT_FROM,ret);
        }
    }
    
    // no "/" or "\" found
    return CseEmptyStr;
}

TBool FileExists(const TXchar *FileName)
{
    WIN32_FIND_DATA FindData;
    HANDLE h = FindFirstFile(FileName, &FindData);

    if (h != INVALID_HANDLE_VALUE)
    {
        FindClose(h);
        return true;
    }
    else return false;
}

TBool DirExists(const TXchar *DirName)
{
    TCharArray sDir = DirName;
    if (sDir.len() <= 0) return false;

    TXchar ch = sDir[-1];
    if (ch != _T('\\') && ch != _T('/'))
        sDir += _T('\\');
    if (FILE_ATTRIBUTE_DIRECTORY == GetFileAttributes(sDir))
        return true;
    else return false;
}

TCharArray NormalizeFileName(const TCharArray& sFile)
{
    TCharArray ret = sFile;
    int iLen = ret.len();
    for (int i = 0; i < iLen; i++)
        if (ret[i] == _T('/')) ret[i] = _T('\\');

#ifdef WINCE
	return ret;
#else
    TXchar buff[260];
    TXchar *FilePart;
    if (GetFullPathName(ret.addr(),260,buff,&FilePart) > 0)
        return buff;
    else return TCharArray(CUT_FROM,ret);
#endif
}

TBool SearchSubDirs(TCharArray& sOut, const TCharArray& dir)
{
    ASSERT_TRUE(! dir.isEmpty());
    sOut.truncate(0);

    TCharArray TargPath = dir;
    TXchar ch = TargPath[-1];
    if (ch != _T('\\') && ch != _T('/'))
        TargPath += _T("\\");

    TCharArray ToFind = TargPath + _T("*.*");
    WIN32_FIND_DATA FindData;
    HANDLE h = FindFirstFile(ToFind.addr(),&FindData);
    if (INVALID_HANDLE_VALUE == h) return false;

    do
    {
        if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
        {
            TCharArray sCurr = FindData.cFileName;
            if (sCurr != _T(".") && sCurr != _T(".."))
                sOut += TargPath + sCurr + _T('\0');
        }
    }
    while (FindNextFile(h,&FindData));
    FindClose(h);

    return true;
}

// Following define TRegConfig
//==============================================================================
const long HKey_ClassesRoot   = (long) HKEY_CLASSES_ROOT;
const long HKey_CurrentUser   = (long) HKEY_CURRENT_USER;
const long HKey_users         = (long) HKEY_USERS;
const long HKey_LocalMachine  = (long) HKEY_LOCAL_MACHINE;

// root and path example:
//   root: HKEY_CURRENT_USER
//   path: Software\VendorName\AppName
//         Software\CseSoft\CseKernel20

class TRegConfig
{
private:
    WIN32_REG_STRUCT m_data;

    TBool open();
    TBool close();
    
public:
    TRegConfig(long root, const TXchar *path);
    ~TRegConfig();

    TBool read(const TXchar *name, char *out, TUint32& iLen);
};

TRegConfig::TRegConfig(long root, const TXchar *path)
{
    TCharArray sPath = path;
    CHECK_TRUE(sPath.len() > 0);

    TXchar ch = sPath[0];
    if (ch == _T('\\') || ch == _T('/'))
    {
        sPath.remove(0);
        CHECK_TRUE(sPath.len() > 0);
    }
    int iMax = sPath.len()-1;
    ch = sPath[iMax];
    if (ch == _T('\\') || ch == _T('/'))
    {
        sPath.remove(iMax);
        CHECK_TRUE(sPath.len() > 0);
    }

    m_data.path     = sPath.transfer(NULL);
    m_data.hRootKey = root;
    m_data.hKey     = 0;
}

TRegConfig::~TRegConfig()
{
    if ( m_data.hKey != 0 ) close();
    TCharArray s;
    s.transfer(m_data.path);
}

TBool TRegConfig::close()
{
    if ( m_data.hKey != 0 )
    {
        if ( RegCloseKey(m_data.hKey) != ERROR_SUCCESS )
        {
            m_data.hKey = 0;
#ifdef _DEBUG
            LOG_MSG(TCharArray::format(_T("Can not close registry key '%s'"),m_data.path));
#endif
            return false;
        }
        else m_data.hKey = 0;
    }
    return true;
}

TBool TRegConfig::open()
{
    if ( m_data.hKey != 0 )
        return true;

    HKEY tmpKey;
    if (RegOpenKeyEx((HKEY)m_data.hRootKey,m_data.path,0,KEY_READ,&tmpKey) != ERROR_SUCCESS)
    {
#ifdef _DEBUG
        LOG_MSG(TCharArray::format(_T("Can not open registry key '%s'"),m_data.path));
#endif
        return false;
    }

    m_data.hKey = tmpKey;
    return true;
}

TBool TRegConfig::read(const TXchar *name, char *out, TUint32& iLen)
{
    CHECK_TRUE(name != NULL && name[0] != _T('\0'));

    TBool ret = false;
    if (open())
    {
        if (RegQueryValueEx(m_data.hKey,name,NULL,NULL,(LPBYTE)out,(LPDWORD)&iLen) == ERROR_SUCCESS )
            ret = true;
    }
    return ret;
}

// Following define TDllFile
//==============================================================================
TDllFile::TDllFile(const TXchar *file)
{
    m_handle = loadLib(file);
}

TDllFile::~TDllFile()
{
    unloadLib(m_handle);
}

void *TDllFile::loadLib(const TXchar *file)
{
    void *handle = (void *) LoadLibrary(file);
    CHECK_MSG(NULL != handle, _T("Load dll file error:") + TCharArray(file));
    return handle;
}

void TDllFile::unloadLib(void *handle)
{
    if (NULL != handle)
        FreeLibrary((HMODULE) handle);
}

TBool TDllFile::isLoaded()
{
    return NULL != m_handle;
}

void TDllFile::attach(void * handle)
{
    ASSERT_TRUE(m_handle == NULL);
    m_handle = handle;
}

void *TDllFile::detach()
{
    void *ret = m_handle;
    m_handle = NULL;
    return ret;
}

void *TDllFile::getSymbol(const TXchar *name)
{
    CHECK_TRUE(m_handle != NULL); 
#ifdef WINCE
    return (void *) GetProcAddress((HMODULE)m_handle,name);         // name must be unicode
#else
    return (void *) GetProcAddress((HMODULE)m_handle,(LPCSTR)name); // name must ansi
#endif
}

TCharArray Cse_GetHomeDir()
{
#if defined(__VISUALC__) || defined(__MINGW32__)
    // first, try find from environment: CSE_HOME
  TXchar buffer[260] = {_T('\0')};
  #ifndef WINCE
    DWORD iSize = GetEnvironmentVariable(CSE_HOME_DIR_NAME,buffer,sizeof(buffer));
    if (iSize > 0) return buffer;
  #endif

    // then, try find in windows regist-table
    TUint32 iLen = sizeof(buffer);
    TRegConfig config(HKey_LocalMachine,CSE_KERNEL_REG_ID);

    if (config.read(_T("HomePath"),(char *)buffer,iLen))
        return buffer;
    else
    {
  #ifdef WINCE
       return WINCE_CSE_LIB_PATH;
  #endif
    }
#endif

    return CseEmptyStr;
}
